package com.octopus.tree.imp.simple;

import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.apache.log4j.Logger;

import com.octopus.object.ObjectUtils;
import com.octopus.string.StringUtils;
import com.octopus.tree.TreeBranch;
import com.octopus.tree.TreeLeaf;
import com.octopus.tree.structure.NodeType;
import com.octopus.tree.structure.XpType;

public class TreeLeafImp extends NodeType implements TreeLeaf, Cloneable,
		Serializable {

	private static final Logger logger = Logger.getLogger(TreeLeafImp.class.getName());
	
	/**
	 *  the default serialVerstionUID
	 */
	private static final long serialVersionUID = 1L;
	
	private Object objectContent; 
	private Boolean initProperties_flag = new Boolean(false);
	private Properties properties = new Properties();
	private TreeBranchImp parent;
	
	@Override
	public Object getObjectContent() {
		return objectContent;
	}

	@Override
	public void setObjectContent(Object objectContent) {
		this.objectContent = objectContent;
	}

	public TreeBranchImp getParent() {
		return parent;
	}

	protected void setParent(TreeBranchImp parent) {
		this.parent = parent;
	}
	
	private TreeLeafImp() {
	}
	
	public TreeLeafImp(String name, String kind, Object object, Properties properties){
		this();
		this.setTreeLeafName(name);
		this.setKind(kind);
		this.setObjectContent(object);
		if(properties != null && !properties.isEmpty()){
			Set<Object> keySet = properties.keySet();
			Iterator<Object> iterator = keySet.iterator();
			while(iterator.hasNext()){
				String key = (String)iterator.next();
				String value = properties.getProperty(key);
				this.setTreeLeafProperty(key, value);
			}
		}
	}
	
	public TreeLeafImp(NodeType nodeType){
		this();
		if(nodeType == null){
			return;
		}
		this.setContent(nodeType.getContent());
		this.setContentType(nodeType.getContentType());
		this.setKind(nodeType.getKind());
		this.setName(nodeType.getName());
		this.getXp().addAll(nodeType.getXp());
	}

	@Override
	public String getTreeLeafName() {
		return this.getName();
	}

	@Override
	public String getTreeLeafKind() {
		return this.getKind();
	}

	@Override
	public String getTreeLeafContentType() {
		return this.getContentType();
	}

	@Override
	public Object getTreeLeafContent() {
		return this.objectContent;
	}
	
	private void initTreeLeafProperties(){
		synchronized(this.initProperties_flag){
			if(!this.initProperties_flag){
				List<XpType> propertiesList = this.getXp();
				if(propertiesList != null){
					for(XpType xt : propertiesList){
						properties.setProperty(xt.getKey(), xt.getValue());
					}
				}
				this.initProperties_flag = new Boolean(true);
			}
		}
	}

	@Override
	public Properties getTreeLeafProperties() {
		if(!this.initProperties_flag){
			initTreeLeafProperties();
		}
		Properties properties = new Properties();
		properties.putAll(this.properties);
		return properties;
	}

	@Override
	public String getTreeLeafProperty(String key) {
		if(!this.initProperties_flag){
			initTreeLeafProperties();
		}
		return this.properties.getProperty(key);
	}

	@Override
	public synchronized void setTreeLeafName(String name) throws DefaultSameNameTreeLeafAlreadyExist {
		this.setName(name);
	}
	
	@Override
	public synchronized void setName(String name) throws DefaultSameNameTreeLeafAlreadyExist{
		StringUtils.StringContainer sc = new StringUtils.StringContainer(name);
		if(StringUtils.isNullString(sc)){
			throw new NameIsNullException();
		}
		if(this.parent == null){
			super.setName(sc.getAfterTrimedString());
		}else{
			if(this.getTreeLeafName() != null && this.getTreeLeafName().equals(sc.getAfterTrimedString())){
				return;
			}			
			synchronized(this.parent.treeLeaf_initflag){
				if(this.getTreeLeafKind() == null){
					this.parent.changeDefaultTreeLeafName(this, sc.getAfterTrimedString());
				}else{
					this.parent.changeNonDefaultTreeLeafName(this, sc.getAfterTrimedString());
				}	
			}
		}
	}

	@Override
	public void setTreeLeafContent(Object object) {
		this.objectContent = object;
	}

	@Override
	public void setTreeLeafProperty(String name, String value) {
		if(name == null){
			return;
		}
		if(!this.initProperties_flag){
			initTreeLeafProperties();
		}
		this.properties.setProperty(name, value);
		boolean alreadyExist = false;
		List<XpType> xpTypeList = this.getXp();
		for(XpType xt : xpTypeList){
			if(name.equals(xt.getKey())){
				xt.setValue(value);
				alreadyExist = true;
			}
		}
		if(!alreadyExist){
			XpType newXt = new XpType();
			newXt.setKey(name);
			newXt.setValue(value);
			this.getXp().add(newXt);
		}
	}

	@Override
	public void removeTreeLeafProperty(String name) {
		if(name == null){
			return;
		}
		if(!this.initProperties_flag){
			initTreeLeafProperties();
		}
		this.properties.remove(name);
		List<XpType> xpTypeList = this.getXp();
		for(int i = xpTypeList.size() - 1; i >= 0 ; i--){
			XpType xt = xpTypeList.get(i);
			if(name.equals(xt.getKey())){
				xpTypeList.remove(i);
			}
		}
	}

	@Override
	public void detach() {
		if(this.parent != null){
			this.parent.removeTreeLeaf(this);
		}
	}
	
	@Override
	public String genTreePath(){
		return StringUtils.concat(genParentPath() ,".", this.getName());
		
	}
	
	@Override
	public String genParentPath(){
		String path = "";
		TreeBranchImp parent = this.getParent();
		int i = 0;
		while(parent != null){
			if(SimpleUtils.isRootBranch(parent)){
				break;
			}
			if(i == 0){
				path = parent.getName();
			}else{
				path = StringUtils.concat(parent.getName() ,".", path);
			}
			parent = parent.getParent();
			i++;
		}
		return path;
	}

	@Override
	public boolean preparePermanent() {
		if(this.objectContent != null){
			this.setContentType(this.objectContent.getClass().getName());
			String content = ObjectUtils.asString(this.objectContent);
			if(content != null){
				this.setContent(content);
				return true;
			}else{
				return false;
			}
		}
		return true;
	}

	@Override
	public void clearPermanent() {
		if(this.objectContent == null){
			this.setContent(null);
			this.setContentType(null);
		}
	}

	@Override
	public boolean toContentObject() {
		Object object = ObjectUtils.asObject(this.getContent());
		if(object != null){
			this.setObjectContent(object);
			return true;
		}
		return false;
	}

	@Override
	public void setTreeLeafKind(String kind) {
		this.setKind(kind);
	}
	
	@Override
	public synchronized void setKind(String kind) {
		if (this.parent == null) {
			StringUtils.StringContainer sc = new StringUtils.StringContainer(
					kind);
			if (StringUtils.isNullString(sc)) {
				super.setKind(null);
			} else {
				super.setKind(sc.getAfterTrimedString());
			}
		} else {
			boolean resetKind = false;
			boolean newIsNull = false;
			boolean oldIsNull = false;
			TreeBranchImp parent = this.getParent();
			String oldKind = this.getKind();
			StringUtils.StringContainer sc = new StringUtils.StringContainer(
					kind);
			if (StringUtils.isNullString(sc)) {
				newIsNull = true;
				if (oldKind == null) {
					oldIsNull = true;
					return;
				} else {
					oldIsNull = false;
					resetKind = true;
				}
			} else {
				String realKind = sc.getAfterTrimedString();
				if (oldKind == null) {
					oldIsNull = true;
					resetKind = true;
				} else {
					oldIsNull = false;
					if (!realKind.equals(oldKind)) {
						resetKind = true;
					} else {
						return;
					}
				}
			}
			if (resetKind) {
				synchronized(parent.treeLeaf_initflag){
					if(newIsNull){
						TreeLeaf oldTreeLeaf = parent.getDefaultTreeLeafByName(this.getName());
						if(oldTreeLeaf != null){
							throw new DefaultSameNameTreeLeafAlreadyExist(StringUtils.concat("Default TreeLeaf[",this.genTreePath(),"] already exists"));
						}
					}
					boolean moveResult = false;
					if(newIsNull){
						moveResult = true;
					}else{
						if(oldIsNull){
							moveResult = parent.moveFromDefaultLeafMap(this);
						}
						moveResult = true;
					}
					if(moveResult){
						if (newIsNull) {
							super.setKind(null);
						} else {
							super.setKind(sc.getAfterTrimedString());
						}
						if(newIsNull){
							moveResult = parent.moveIntoDefaultLeafMap(this);
						}else{
							moveResult = true;
						}
						if(!moveResult){
							logger.fatal(StringUtils.concat("[TreeLeaf][MOVE INTO][",this.genTreePath(),"] set kind from [",this.getKind(),"] to [",kind,"] failed bug"));
						}
					}else{
						logger.fatal(StringUtils.concat("[TreeLeaf][MOVE OUT][",this.genTreePath(),"] set kind from [",this.getKind(),"] to [",kind,"] failed bug"));
					}
				}
			}
		}
	}
	
	void changeMyName(String name){
		super.setName(name);
	}
}
